Day 7 - Swift Functions - 1 : Parameters and Return
Table of Contents
What is Function? #
We can write functions separately from the rest of our code and call them repeatedly as needed.
For example, suppose we have a code group like this;
print("Welcome to my app!")
print("By default This prints out a conversion")
print("chart from centimeters to inches, but you")
print("can also set a custom range if you want.")
If we have to use these print()
statements in 3 different places in our project, will we constantly copy-paste? What if we need to make a change? Do we have to go to each place where we copied and pasted and make these corrections? For problems like these, Swift - like other languages - has a Functions solution.
When we convert the above code into a function, it looks like this;
func showWelcome() {
print("Welcome to my app!")
print("By default This prints out a conversion")
print("chart from centimeters to inches, but you")
print("can also set a custom range if you want.")
}
Let’s examine the code above;
- To mark the start of a function, the keyword
func
is used. - We name the function
showWelcome
. This can be any name you want. - The body of the function is enclosed between the brackets
{ }
.
The ()
parentheses are used when creating and calling functions.
Calling the created function is done as follows.
showWelcome()
So what do parentheses really do? Parentheses are used to customize the operation of the function.
let number = 139
if number.isMultiple(of: 2) {
print("Even")
} else {
print("Odd")
}
In our example, the function .isMultiple(of:2)
takes the value 2 as a parameter and checks whether the variable number
is a multiple of 2. If .isMultiple(of:)
did not take any parameters, it would be a bit silly.
Function Parameters #
We can create configurable functions by putting extra code between the ()
parentheses. Let’s create a function that multiplies the given number by the numbers from 1 to 12 and prints the result on the screen. (In our example, we used 5 when calling the function, you can choose whatever you want.)
func printTimesTables(number: Int) {
for i in 1...12 {
print("\(i) x \(number) is \(i * number)")
}
}
printTimesTables(number: 5)
//OUTPUT:
//----------------------------------------
// 1 x 5 is 5
// 2 x 5 is 10
// 3 x 5 is 15
// 4 x 5 is 20
// 5 x 5 is 25
// 6 x 5 is 30
// 7 x 5 is 35
// 8 x 5 is 40
// 9 x 5 is 45
// 10 x 5 is 50
// 11 x 5 is 55
// 12 x 5 is 60
Where we define a function, number: Int
is the parameter of our function. We declare that the caller of this function should enter an integer here. Inside the function, the number
variable is used as an integer.
When calling printTimesTables()
, we need to write the parameter name along with the function call (number: 5)
. Writing the parameter name makes it easier for multi-parameter functions.
func printTimesTables(number: Int, end: Int) {
for i in 1...end {
print("\(i) x \(number) is \(i * number)")
}
}
printTimesTables(number: 5, end: 20)
In general, the variables specified when defining a function are named parameters, and the variables passed when calling a function are named arguments. Actually there is not much difference in the meaning of these names, you can see that some people call both parameters and arguments.
Note: Any data created inside the function is destroyed after the function completes.
Return Value of Functions (return) #
We have seen how we can create functions. But often functions can also send data back.
The sqrt()
function in the Cocoa library can give us the square root of a number.
let root = sqrt(169)
print(root)
//OUTPUT:
//----------------------------------------
// 13.0
The sqrt()
function accepts one parameter, and returns the square root of the number it receives.
If we want to return a value in our function, here is what we need to do;
- Make an arrow before the initial curly brackets of our function and write which data type it will return.
- Use the keyword
return
to return the data.
func rollDice() -> Int {
return Int.random(in: 1...6)
}
let result = rollDice()
print(result)
In the above example, we have declared that the rollDice()
function will return a value of type Int
, so we must return a value of type Int
with return
.
Let’s take an example;
Our function takes two Strings as parameters, looks at the letters of the two Strings and checks if they are the same letters, if they are the same letters, it returns true
. For example, the result of Strings “abc” and “cab” should be true.
func areLettersIdentical(string1: String, string2: String) -> Bool {
let first = string1.sorted()
let second = string2.sorted()
return first == second
}
We can simplify the above code a bit more. We can omit the first
and second
variables.
func areLettersIdentical(string1: String, string2: String) -> Bool {
return string1.sorted() == string2.sorted()
}
We can shorten the above code even more;
func areLettersIdentical(string1: String, string2: String) -> Bool {
string1.sorted() == string2.sorted()
}
There is no need to use return
in single-line functions. This only works when the function contains a single line of code and that line of code actually returns the data you specified to return. So there is no need for return
when using a single line expression.
func greet(name: String) -> String {
if name == "Taylor Swift" {
return "Oh wow!"
} else {
return "Hello, \(name)"
}
}
When we want to remove return
from the above code, we cannot do that because return
comes from a statement.But if we refactor our code using ternary operator we can remove return
.
func greet(name: String) -> String {
name == "Taylor Swift" ? "Oh wow!" : "Hello, \(name)"
}
TIP : We can use the return
keyword even if our function does not return a value. In this case we force the function to exit.
Difference between Expression and Statements #
When our code can be reduced to a single value such as true, false, “Hello” or 19, we call it an expression. An expression is something that can be assigned to a variable or printed using print()
.
let isAdmin = true
let isOwner = false
let isEditingEnabled = false
//expression
isOwner == true && isEditingEnabled || isAdmin == true
//all returns true.
On the other hand, actions like creating a variable, starting a loop or checking a condition are called statements.
//statement
let name = "Otis"
Returning Multiple Values from a Function #
A tuple is used to return multiple values from functions. Like Array, Dictionary and Set, Tuples allow us to put multiple data into a single variable. However, unlike the others, Tuples can have a fixed size and multiple data types.
Of course, we can also use Array or Dictionary for multiple returns. But this can be ugly and confusing. Because in Array we need to know the index of the data we want, while in Dictionary we need to know both the key and provide the default value. Tuples, on the other hand, can be used very well in functions to return multiple values.
func getUser() -> (firstName: String, lastName: String) {
(firstName: "Taylor", lastName: "Swift")
}
let user = getUser()
print("Name: \(user.firstName) \(user.lastName)")
Now let’s examine the code above;
- Our return type:
(firstName: String, lastName: String)
so a Tuple containing two Strings - Each String used in Tuple has a unique name. Unlike key in Dictionary, this name is not enclosed in "" quotes.
- Inside our function, we specify and send the values of all the elements we have promised to return.
- When we call
getUser()
we can read the values of the tuple by giving key.
Yes, at this point we can think that tuples are similar to Dictionary, but there are some differences;
- When we access values in a Dictionary, Swift doesn’t know in advance whether they exist or not. When we give the key of the Dictionary, we may know at the time that something may be returned, but Swift can’t be sure, so we need to provide an default value.
- When we access the value in a tuple, Swift knows in advance that the value of the tuple is available, so we don’t need to provide an default value.
- We can access the values as
user.firstName
. This is not a String, so there is no chance of a typo. (You can see that Xcode suggests to complete the code after typing user.) - Dictionary can contain many different values, but Tuple cannot. A Tuple must list all the data it will contain, contain all of it and nothing else. This means that Tuple is assigned values when it is first created and cannot be changed.
This is where Tuple has an important advantage over Dictionary: we can specify exactly which values will be present and what types they have, whereas Dictionary may or may not contain the values we want.
Things to Consider When Using Tuple #
- When returning Tuple from a function, Swift already knows the Tuple element names. Therefore, we don’t need to write them again. We can also write the above example as follows.
func getUser() -> (firstName: String, lastName: String) {
("Taylor", "Swift")
}
- Sometimes we may see Tuple usages where there are no element names. In this case we can access Tuple elements using numeric indices starting from 0. These numeric indices can also be used in named Tuples.
func getUser() -> (String, String) {
("Taylor", "Swift")
}
let user = getUser()
print("Name: \(user.0) \(user.1)")
- If the function returns a Tuple, we can split the Tuple into individual values. Actually, there is nothing new here, we have just moved the data around a bit.
func getUser() -> (firstName: String, lastName: String) {
(firstName: "Taylor", lastName: "Swift")
}
let user = getUser()
let firstName = user.firstName
let lastName = user.lastName
print("Name: \(firstName) \(lastName)")
However, instead of assigning the Tuple to user
and copying the values one by one, we can split the Tuple returned from getUser()
into two separate constants.
let (firstName, lastName) = getUser()
print("Name: \(firstName) \(lastName)")
Finally, if we don’t need all the values in the returned Tuple, we use _
to tell Swift to ignore part of the Tuple.
let (firstName, _) = getUser()
print("Name: \(firstName)")
Customizing Function Parameter Naming #
External and Internal parameter names of functions created in Swift can be specified separately. External name is used when calling the function, internal name is used when performing operations inside the body of the function.
In Swift, external parameter naming of functions is so common that these names are used when deciding which method to call.
func hireEmployee(name: String) { }
func hireEmployee(title: String) { }
func hireEmployee(location: String) { }
Yes, all of the above are hireEmployee()
functions. But Swift knows which one we mean by the parameter name we give it.
Sometimes we don’t want to give a parameter name when calling a function. Consider the hasPrefix()
function
let lyric = "I see a red door and I want it painted black"
print(lyric.hasPrefix("I see"))
Here in the hasPrefix()
function, _
allows us to use the parameter name. We can also use it in the functions we create. Notice that _
is used instead of an external parameter name.
func isUppercase(_ string: String) -> Bool {
string == string.uppercased()
}
let string = "HELLO, WORLD"
let result = isUppercase(string)
Sometimes we may also want to use external parameter names to increase the readability of our code
func printTimesTables(number: Int) {
for i in 1...12 {
print("\(i) x \(number) is \(i * number)")
}
}
printTimesTables(number: 5)
The code above is a valid code for Swift. But it is a bit weak in readability. We can rewrite the above code using external parameter naming to improve readability.
func printTimesTables(for number: Int) {
for i in 1...12 {
print("\(i) x \(number) is \(i * number)")
}
}
printTimesTables(for: 5)
Let’s take a closer look at what we need to pay attention to here;
for number: Int
, the external name isfor
and the internal name isnumber
.- When we call the function, we use the external name for the parameter:
printTimesTables(for: 5)
- Inside the function, we use the internal name:
print("\(i) x \(number) is \(i * number)")
You can also read this article in Turkish.
Bu yazıyı Türkçe olarak da okuyabilirsiniz.